﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting;
using MessageCallAspect.Inspector.MessageSink;

namespace MessageCallAspect.Inspector
{

    public static class ReflectionExtensions
    {
        public static T[] GetAllAttributes<T>(this ICustomAttributeProvider member)
          where T : Attribute
        {
            return member.GetCustomAttributes(typeof(T), true) as T[];
        }

        public static bool HasAttribute<T>(this ICustomAttributeProvider member)
            where T : Attribute
        {
            return member.IsDefined(typeof(T), true);
        }

        public static MethodInfo GetInterfaceMethod<TInterface>(this MethodInfo mi)
        {
            InterfaceMapping interfaceMapping = mi.ReflectedType.GetInterfaceMap(typeof(TInterface));
            for (int i = 0; i < interfaceMapping.TargetMethods.Length; i++)
            {
                if (interfaceMapping.TargetMethods[i] == mi)
                {
                    return interfaceMapping.InterfaceMethods[i];
                }
            }
            return null;
        }
        public static IEnumerable<MethodInfo> GetInterfaceConcreteMethods<TInterface>(this Type type)
        {
            InterfaceMapping interfaceMapping = type.GetInterfaceMap(typeof(TInterface));
            foreach (MethodInfo mi in interfaceMapping.TargetMethods)
            {
                yield return mi;
            }
        }

    }

    public static class MessagingExtensions
    {
        public static void ValidateParameters(this IMessage msg)
        {
            IMethodCallMessage mcm = (IMethodCallMessage)msg;

            MethodInfo mi = (MethodInfo)mcm.MethodBase;

            ParameterInfo[] parameters = mi.GetParameters();
            int narg = 0;

            foreach (ParameterInfo pi in parameters)
            {
                object pval = mcm.GetArg(narg++);
                Type pt = pi.ParameterType;
                if (pt.IsByRef) pt = pt.GetElementType();

                if (pval != null && !pt.IsInstanceOfType(pval))
                    throw new RemotingException("Cannot cast argument " + pi.Position + " of type '" + pval.GetType().AssemblyQualifiedName +
                            "' to type '" + pt.AssemblyQualifiedName + "'");
            }
        }

        public static ReturnMessage Wrap(this Exception ex, IMessage msg)
        {
            if ((ex is RemotingException || ex is TargetInvocationException)
                  && ex.InnerException != null)
                ex = ex.InnerException;
            return new ReturnMessage(ex, (IMethodCallMessage)msg);
        }
    }

    public static class LogicExtensions
    {
        public static Func<T, bool> AndAlso<T>(this Func<T, bool> predicate1, Func<T, bool> predicate2)
        {
            return arg => predicate1(arg) && predicate2(arg);
        }
        public static Func<T, bool> OrElse<T>(this Func<T, bool> predicate1, Func<T, bool> predicate2)
        {
            return arg => predicate1(arg) || predicate2(arg);
        }
    }
}
